﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;

namespace FirstFloor.Documents.Pdf
{
    /// <summary>
    /// Represents a PDF array object.
    /// </summary>
    internal class ArrayObject
        : PdfObject
    {
        private List<object> elements = new List<object>();

        /// <summary>
        /// Initializes a new instance of the <see cref="ArrayObject"/> class.
        /// </summary>
        /// <param name="owner">The owner.</param>
        public ArrayObject(PdfParser owner)
        {
            this.Owner = owner;
        }

        /// <summary>
        /// Gets the PDF parser that owns this dictionary object.
        /// </summary>
        /// <value>The owner.</value>
        public PdfParser Owner { get; private set; }

        /// <summary>
        /// Gets the number of entries contained in this array object.
        /// </summary>
        /// <value>The count.</value>
        public int Count
        {
            get { return this.elements.Count; }
        }

        /// <summary>
        /// Gets the <see cref="System.Object"/> at specified index.
        /// </summary>
        /// <value></value>
        public object this[int index]
        {
            get { return Get<object>(index); }
        }

        /// <summary>
        /// Gets the entry at specified index.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="index">The index.</param>
        /// <returns></returns>
        public T Get<T>(int index)
        {
            var o = this.elements[index];
            if (o is ObjectIdentifier) {
                // indirect object reference found, parse it
                o = this.Owner.ParseIndirectObject<T>((ObjectIdentifier)o);
                // replace element in the array object with the actual value
                // the next lookup will immediately get the value
                this.elements[index] = o;
            }
            return ParserUtility.Cast<T>(o);
        }

        /// <summary>
        /// Gets the typed elements of this array object.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public T[] ToArray<T>()
        {
            return ParserUtility.ToArray<T>(this.elements.ToArray());
        }

        internal void Add(object o)
        {
            this.elements.Add(o);
        }

#if DEBUG
        /// <summary>
        /// Writes the contents of the dictionary object to debug output.
        /// </summary>
        internal void WriteContents()
        {
            var output = new StringBuilder();
            WriteContents(output, 0);
            Debug.WriteLine(output.ToString());
        }

        /// <summary>
        /// Writes the contents of the dictionary object to the specified output.
        /// </summary>
        /// <param name="output">The output.</param>
        /// <param name="indent">The indent.</param>
        internal void WriteContents(StringBuilder output, int indent)
        {
            for (int i = 0; i < this.elements.Count; i++) {
                // indent
                output.Append(new string(' ', indent * 2));
                output.AppendFormat("[{0}]: ", i);

                var value = this[i];

                var dict = value as DictionaryObject;
                if (dict != null) {
                    output.AppendLine("(DictionaryObject)");
                    dict.WriteContents(output, indent + 1);
                    continue;
                }
                var arr = value as ArrayObject;
                if (arr != null) {
                    output.AppendLine("(ArrayObject)");
                    arr.WriteContents(output, indent + 1);
                    continue;
                }
                output.AppendFormat("{0}\r\n", value);
            }
        }
#endif
    }
}
